/*
@STI LICENSE
(C) 2006-2008 Universit�t Innsbruck.

This file is part of myOntology.

myOntologyis free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

myOntologyis distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with myOntology.  If not, see <http://www.gnu.org/licenses/>.

@STI LICENSE */
package controller.elementMapper;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Iterator;

import model.Vocabulary;
import model.dataContainer.TranslationContainer;

import org.apache.log4j.Logger;
import org.myontology.controller.AttributeHolder;
import org.myontology.specialpages.helpers.LanguageCheck;
import org.openrdf.model.URI;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.RepositoryConnection;

import controller.HelperFunctions;
import controller.sesameDataConnector.AbstractDataConnector;
import controller.sesameDataConnector.ControllerException;

public class Mapper {

	public static Logger logger = Logger.getLogger(Mapper.class);
	
	protected ArrayList<Exception> exceptionsLabel = null;
	protected ArrayList<Exception> exceptionsComment = null;
	protected ArrayList<Exception> exceptionsSeeAlsos = null;
	protected ArrayList<Exception> exceptionsImages = null;
	protected ArrayList<Exception> exceptionsVideos = null;
	protected ArrayList<Exception> exceptionsSynonyms = null;
	protected ArrayList<Exception> exceptionsTags = null;
	protected ArrayList<Exception> exceptionsTranslations = null;
	protected ArrayList<Exception> exceptionsExamples = null;
	protected ArrayList<Exception> exceptionsBelongsToOM = null;
	protected ArrayList<Exception> exceptionsSameAs = null;
	protected ArrayList<Exception> exceptionsPartlyOverlapsWith = null;
	protected ArrayList<Exception> exceptionNarrowerThan = null;
	protected ArrayList<Exception> exceptionBroaderThan = null;
	
	protected boolean exceptionOccured;
	
	public boolean exceptionsOccured() {
		return this.exceptionOccured;
	}
	
	public Mapper() {
		this.exceptionOccured = false;
	}

	protected void addExceptionLabel( Exception ex ) {
		
		if( this.exceptionsLabel==null )
			this.exceptionsLabel = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsLabel.add( ex );
	}
	public ArrayList<Exception> getExceptionLabel() {
		return this.exceptionsLabel;
	}
	
	protected void addExceptionComment( Exception ex ) {
		
		if( this.exceptionsComment==null )
			this.exceptionsComment = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsComment.add( ex );
	}
	public ArrayList<Exception> getExceptionComment() {
		return this.exceptionsComment;
	}
	
	protected void addExceptionSeeAlsos( Exception ex ) {
		
		if( this.exceptionsSeeAlsos==null )
			this.exceptionsSeeAlsos = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsSeeAlsos.add( ex );
	}
	public ArrayList<Exception> getExceptionsSeeAlsos() {
		return this.exceptionsSeeAlsos;
	}
	
	protected void addExceptionsImages( Exception ex ) {
		
		if( this.exceptionsImages==null )
			this.exceptionsImages = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsImages.add( ex );
	}
	public ArrayList<Exception> getExceptionsImages() {
		return this.exceptionsImages;
	}
	
	protected void addExceptionsVideos( Exception ex ) {
		
		if( this.exceptionsVideos==null )
			this.exceptionsVideos = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsVideos.add( ex );
	}
	public ArrayList<Exception> getExceptionsVideos() {
		return this.exceptionsImages;
	}

	protected void addExceptionsSynonyms( Exception ex ) {
		
		if( this.exceptionsSynonyms==null )
			this.exceptionsSynonyms = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsSynonyms.add( ex );
	}
	public ArrayList<Exception> getExceptionsSynonyms() {
		return this.exceptionsSynonyms;
	}

	protected void addExceptionsTags( Exception ex ) {
		
		if( this.exceptionsTags==null )
			this.exceptionsTags = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsTags.add( ex );
	}
	public ArrayList<Exception> getExceptionsTags() {
		return this.exceptionsTags;
	}

	protected void addExceptionsTranslations( Exception ex ) {
		
		if( this.exceptionsTranslations==null )
			this.exceptionsTranslations = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsTranslations.add( ex );
	}
	public ArrayList<Exception> getExceptionsTranslations() {
		return this.exceptionsTranslations;
	}
	
	protected void addExceptionsExamples( Exception ex ) {
		
		if( this.exceptionsExamples==null )
			this.exceptionsExamples = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsExamples.add( ex );
	}
	public ArrayList<Exception> getExceptionsExamples() {
		return this.exceptionsExamples;
	}

	protected void addExceptionBelongsToOM( Exception ex ) {
		
		if( this.exceptionsBelongsToOM==null )
			this.exceptionsBelongsToOM = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsBelongsToOM.add( ex );
	}
	public ArrayList<Exception> getExceptionBelongsToOM() {
		return this.exceptionsBelongsToOM;
	}

	protected void addExceptionSameAs( Exception ex ) {
		
		if( this.exceptionsSameAs==null )
			this.exceptionsSameAs = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsSameAs.add( ex );
	}
	public ArrayList<Exception> getExceptionSameAs() {
		return this.exceptionsSameAs;
	}

	protected void addExceptionPartlyOverlapsWith( Exception ex ) {
		
		if( this.exceptionsPartlyOverlapsWith==null )
			this.exceptionsPartlyOverlapsWith = new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionsPartlyOverlapsWith.add( ex );
	}
	public ArrayList<Exception> getExceptionPartlyOverlapsWith() {
		return this.exceptionsPartlyOverlapsWith;
	}

	protected void addExceptionNarrowerThan( Exception ex ) {
		
		if( this.exceptionNarrowerThan==null )
			this.exceptionNarrowerThan= new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionNarrowerThan.add( ex );
	}
	public ArrayList<Exception> getExceptionNarrowerThan() {
		return this.exceptionNarrowerThan;
	}

	protected void addExceptionBroaderThan( Exception ex ) {
		
		if( this.exceptionBroaderThan==null )
			this.exceptionBroaderThan= new ArrayList<Exception>();
		this.exceptionOccured = true;
		this.exceptionBroaderThan.add( ex );
	}
	public ArrayList<Exception> getExceptionBroaderThan() {
		return this.exceptionBroaderThan;
	}



	
	public static boolean isDeprecated( URI uri ) throws Exception {

		boolean ret = false;
		
		String tmp = AbstractDataConnector.selectLiteralObject( uri, Vocabulary.DEPRECATED, null );
		
		if( tmp!=null ) {
			
			ret = true;
		}
			
		return ret;
	}
	
	public static boolean markAsDeprecated( URI uri ) throws Exception {

		boolean ret = false;
		
		if( uri!=null ) {
			try {
				RepositoryConnection con = AbstractDataConnector.getConnection();
				
				try {
					
					// System.out.println( "insert tripel: " + s.stringValue() + " " + p.stringValue() + " " + o.stringValue());				
					
					con.add( uri, Vocabulary.DEPRECATED, new LiteralImpl("true") );
					
					con.commit();
					
					ret = true;
				} catch( Exception e ) {
					con.rollback();
					throw e;	
				} finally {
					con.close();
				}
			} catch (Exception e) {
				logger.error("Error during marking as depricated",e);				
				throw e;
			}
		}
			
		return ret;
	}
	
	public static boolean markAsNonDeprecated( URI uri ) throws Exception {

		boolean ret = false;
		
		if( uri!=null ) {
			try {
				RepositoryConnection con = AbstractDataConnector.getConnection();
				
				try {
					
					// System.out.println( "insert tripel: " + s.stringValue() + " " + p.stringValue() + " " + o.stringValue());				
					
					con.remove( uri, Vocabulary.DEPRECATED, null );
					
					con.commit();
					
					ret = true;
				} catch( Exception e ) {
					con.rollback();
					throw e;	
				} finally {
					con.close();
				}
			} catch (Exception e) {
				logger.error("Error during marking as depricated",e);				
				throw e;
			}
		}
			
		return ret;
	}

	
	
	
	public static void copyTriples( URI property, URI object, URI newId ) throws Exception {
		ArrayList<URI> list = AbstractDataConnector.selectSubjects( property, object );
		
		if( list!=null) {
			java.util.Iterator<URI> iter = list.iterator();
			while( iter.hasNext() ) {
	
				URI moProp = (URI)iter.next();
				AbstractDataConnector.insertTripel( moProp, property, newId );
			}
		}
	}


	/**
	 * inserts a element into the sesame store, returns (if success) the new generated URI of the element
	 * @param data an object of type AbstractElement
	 * @param lang the language in which the element should be inserted
	 * @return the URI of the new inserted element if the element was inserted (else null)
	 * @throws ControllerException
	 *//*
	public URI insert( RepositoryConnection con, AbstractElement data, String lang) throws Exception {
		URI ret = null;
	
		URI uri = AbstractDataConnector.createURI(data);		

		try {
			
			//check wether the id exists allready in the sesame data store
			String query = "SELECT * FROM {<" + uri.stringValue() + ">} p {x}";
			TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
			TupleQueryResult result = tupleQuery.evaluate();
			
			try {
				if ( result.hasNext() ){
					throw new ControllerException("URI for " + data.getLabel() + " exists already!");
				}
			} finally {
				result.close();
			}
			
	
			//insert the element, first check what element it is (OM, C, DTP, OP)
			if (data instanceof OntologyModuleContainer)
				con.add(uri, RDF.TYPE, Vocabulary.ONTOLOGYMODULE);
			else if (data instanceof ClassContainer)
				con.add(uri, RDF.TYPE, Vocabulary.CLASS);
			else if (data instanceof DatatypePropertyContainer) 
				con.add(uri, RDF.TYPE, Vocabulary.DATATYPEPROPERTY);
			else if (data instanceof ObjectPropertyContainer)
				con.add(uri, RDF.TYPE, Vocabulary.OBJECTPROPERTY);
			
			//insert hasVersionDateTime				
			con.add(uri, Vocabulary.HASVERSIONDATETIME, new LiteralImpl( HelperFunctions.convertIntoString( new GregorianCalendar().getTimeInMillis() ) ) );

			
			this.insertLabel(con, uri, data.getLabel(), lang);
			this.insertComment(con, uri, data.getComment(), lang);
			this.insertSeeAlso(con, uri, data.getSeeAlso() );
			this.insertComment(con, uri, data.getComment(), lang);
	
			AbstractDataConnector.addURIs(con, uri, Vocabulary.SEEALSO, data.getSeeAlso() );

			AbstractDataConnector.addTranslations(con, uri, data.getTranslations() );
			
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.SYNONYM, data.getSynonyms() );
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.HASTAG, data.getTags() );
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.FLICKRIMAGE, data.getFlickrImages() );
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.YOUTUBEVIDEO, data.getYouTubeVideos() );
			
			ret = uri;
			
		} catch (Exception e) {
			con.rollback();
			throw e;
		}
	
		return ret;
	}
	*/

	public URI createElement( RepositoryConnection con, URI type, String newLabel, URI oldUri,boolean checkEnglish ) throws Exception {
		
		URI ret = this.createElement(con, type, newLabel,checkEnglish);
		
		if( ret!=null ) {	
			// mark the old ID, that it was renamed:
			con.add( oldUri, Vocabulary.WASRENAMEDTO, ret );
		}
		
		return ret;		
	}
	
public URI createElement( RepositoryConnection con, URI type, String newLabel, URI oldUri ) throws Exception {
		
		return createElement(con, type, newLabel, oldUri, true);	
	}
	
	
	public URI createElement( RepositoryConnection con, URI type, String label,boolean checkEnglish ) throws Exception {
		AttributeHolder a = null;
		return createElement(con, type, label,a,checkEnglish);
	}
	
	public URI createElement( RepositoryConnection con, URI type, String label ) throws Exception {
		
		return createElement(con, type, label,true);
	}
	public URI createElement( RepositoryConnection con, URI type, String label,AttributeHolder attributes ) throws Exception {
		return createElement(con,type,label,attributes,true);
	}
	
	public URI createElement( RepositoryConnection con, URI type, String label,AttributeHolder attributes ,boolean checkEnglish) throws Exception {

		URI ret = null;
		
		label = trimEntry(label);
		//label = label/*.toLowerCase()*/;
		
		this.exceptionsLabel = null;
		
		try {

			checkLabelFormat( label,attributes,checkEnglish );
			
			if( exceptionsLabel==null ) {

				ret = AbstractDataConnector.createURI( type, label );		
		
				
				//check wether the id exists allready in the sesame data store
				String query = "SELECT x FROM {<" + ret.stringValue() + ">} p {x} LIMIT 1";
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				
				try {
					if ( result.hasNext() ){
						throw new UriAlreadyExistsException( ret, label );
					}
				} finally {
					result.close();
				}			
	
				con.add( ret, RDF.TYPE, type );
				
				//insert hasVersionDateTime				
				con.add( ret, Vocabulary.HASVERSIONDATETIME, new LiteralImpl( HelperFunctions.convertIntoString( new GregorianCalendar().getTimeInMillis() ) ) );
				
			}
		}
		catch( Exception e ) {
			

			
			this.addExceptionLabel( e );
			
			ret = null;
		}
		
		return ret;
	}

	/**
	 * Generate Objects of Triples with archiveable objects.
	 * @param p	the predicate
	 * @param o	the uri of the object; can also be a uri of an archived version 
	 * @param type	the type of the returned subjects
	 * @return
	 */
	protected static ArrayList<URI> generateSubjectsOfArchiveableObject( URI p, URI o ) throws Exception {
		ArrayList<URI> ret = null;
	
		URI moreActualVersion = o;
		
		do {

			ArrayList<URI> tmp = AbstractDataConnector.selectSubjects( p, moreActualVersion );
		
			if( tmp!=null ) {
				ret = tmp;
				break;
			}		

			moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, moreActualVersion );
			
		} while( moreActualVersion!=null );

		if( ret!=null && ret.get(0).equals(Vocabulary.NULL) )
			ret = null;
	
		return ret;
	}

	
	
	/**
	 * Generate Objects of Triples with archiveable objects.
	 * @param s	the uri of the subject; can also be a uri of an archived version 
	 * @return
	 */
	protected static ArrayList<URI> generateURIObjects( URI s, URI p ) throws Exception {
		ArrayList<URI> ret = null;

		URI moreActualVersion = s;
		
		do {

			ArrayList<URI> tmp = AbstractDataConnector.selectObjects( moreActualVersion, p );
		
			if( tmp!=null ) {
				ret = tmp;
				break;
			}		

			moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, moreActualVersion );
			
		} while( moreActualVersion!=null );


		if( ret!=null && ret.get(0).equals(Vocabulary.NULL) )
			ret = null;
	
		return ret;
	}
	
	/**
	 * 
	 * @param s	the uri of the subject; can also be a uri of an archived version 
	 * @return
	 */
	protected static ArrayList<String> generateLiteralObjects(URI s, URI p, String lang ) throws Exception {
		ArrayList<String> ret = null;
	
		ret = AbstractDataConnector.selectLiteralObjects(s, p, lang);
		
		URI moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, s );
		
		while( ret==null && moreActualVersion!=null ) {

			ArrayList<String> tmp = AbstractDataConnector.selectLiteralObjects( moreActualVersion, p, lang );
			
			if( tmp!=null )
				ret = tmp;
			
			moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, moreActualVersion );
		}

		if( ret!=null && ret.get(0).equals("") )
			ret = null;
	
		return ret;
	}
	
	/**
	 * 
	 * @param s	the uri of the subject; can also be a uri of an archived version 
	 * @return
	 */
	protected static ArrayList<TranslationContainer> generateTranslations( URI s ) throws Exception {
		ArrayList<TranslationContainer> ret = null;
	
		ret = AbstractDataConnector.selectTranslationsPlain( s );
		
		URI moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, s );
		
		while( ret==null && moreActualVersion!=null ) {

			ArrayList<TranslationContainer> tmp = AbstractDataConnector.selectTranslationsPlain( moreActualVersion );
		
			if( tmp!=null )
				ret = tmp;
			
			moreActualVersion = AbstractDataConnector.selectSubject( Vocabulary.HASPREVIOUSVERSION, moreActualVersion );
		}
		

		if( ret!=null && (ret.get(0)).getValue().equals("") && (ret.get(0)).getLanguage() == null)
			ret = null;
	
		return ret;
	}
	

	
	

	/**
	 * Returns the label [0] and the comment [1] of the uri, if something is found.
	 * @param uri	The element to which to find the label and comment
	 * @param lang	The language in which the label and comment should be found
	 * @return		[0]: label; [1]: comment
	 */
	public static java.util.Date selectHasVersionDateTime(URI uri ) {
		java.util.Date ret = null;

		if( uri != null ) {

			try {
				
				String tmp = AbstractDataConnector.selectLiteralObject(uri, Vocabulary.HASVERSIONDATETIME, null);
				if( tmp!=null )
					ret = HelperFunctions.convertIntoDate( tmp );
				else {
					
					logger.warn( "BUG: normally each element should have a hasVersionDateTime! - selectHasVersionDateTime cannot find date to uri <" + uri + ">." );
					ret = new GregorianCalendar().getTime();
				}

			}
			catch( Exception e )	{
				logger.error( "getType() ",e );

			}
		}
		
		return ret;
	}
	/**
	 * Returns the label [0] and the comment [1] of the uri, if something is found.
	 * @param uri	The element to which to find the label and comment
	 * @param lang	The language in which the label and comment should be found
	 * @return		[0]: label; [1]: comment
	 */
	public static String selectHasVersionDateTimeString(URI uri ) {
		String ret = null;

		if( uri != null ) {

			try {
				
				ret = AbstractDataConnector.selectLiteralObject(uri, Vocabulary.HASVERSIONDATETIME, null);

			}
			catch( Exception e )	{
				logger.error( "getType() ",e );

			}
		}
		
		return ret;
	}
	
	protected static String trimEntry( String entry ) {
		
		if( entry==null )
			entry = "";
		else
			entry = entry.trim();
		
		return entry;
	}

	public static String selectLabel( URI uri, String lang ) throws Exception {
		String ret = null;
		
		if( uri!=null ) {
			
			if(uri.equals(Vocabulary.XMLSBOOLEAN))
				ret = "boolean";
			else if(uri.equals(Vocabulary.XMLSDATETIME))
				ret = "date-time";
			else if(uri.equals(Vocabulary.XMLSDOUBLE))
				ret = "double";
			else if(uri.equals(Vocabulary.XMLSINTEGER))
				ret = "integer";
			else if(uri.equals(Vocabulary.XMLSSTRING))
				ret = "string";	
			else {
				
				ArrayList<String> tmp = generateLiteralObjects( uri, org.openrdf.model.vocabulary.RDFS.LABEL, lang);
				
				if( tmp!=null && tmp.size()!=0 )
					ret = tmp.get(0);
			}
		}
			
		return ret;
	}
	public boolean insertLabel( RepositoryConnection con, URI uri, String entry, String lang ) throws Exception {

		boolean ret = true;
		
		entry = trimEntry(entry);
		//entry = entry/*.toLowerCase()*/;
				
		try {

			checkLabelFormat( entry,false );
			
			if( exceptionsLabel==null ) {
				
				con.add( uri, RDFS.LABEL, new LiteralImpl(entry, lang));
			}
		}
		catch( Exception ex ) {
			this.addExceptionLabel( ex );
		}
		
		if( this.exceptionsLabel!=null )
			ret = false;
		
		return ret;
	}
	
	protected void checkLabelFormat (String newEntry, AttributeHolder request,boolean checkEnglish) throws Exception {
		if( newEntry==null || newEntry.equals("")) {
			this.addExceptionLabel( new WrongFormatException( "Label must not be empty!" ) );
		}
		else {
			
			/*
			if( newEntry.contains( " " ) ) {
				this.addExceptionLabel( new WrongFormatException( "Spaces are not allowed!" ) );
			}
			*/
			
			if( newEntry.contains("\"") || newEntry.contains("'") )
				this.addExceptionLabel( new WrongFormatException( "Colons are not allowed!" ) );
			
			if( newEntry.contains("/") || newEntry.contains("\\") )
				this.addExceptionLabel( new WrongFormatException( "Slashes are not allowed!" ) );
			if (checkEnglish) {
			LanguageCheck check = new LanguageCheck();
				if ((request!=null && !check.checkLabel(newEntry,request))|| (request==null && !check.isEnglish(newEntry))) {
					this.addExceptionLabel(new WrongFormatException("Label does not seem to be in English!"));
				}
			}
		}
	}
	
	protected void checkLabelFormat( String newEntry,boolean checkEnglish ) throws Exception {
		checkLabelFormat(newEntry,null,checkEnglish);
		
	}
	
	public static String selectComment( URI uri, String lang ) throws Exception {
		String ret = null;
		
		ArrayList<String> tmp = generateLiteralObjects( uri, org.openrdf.model.vocabulary.RDFS.COMMENT, lang);
		
		if( tmp!=null && tmp.size()!=0 )
			ret = tmp.get(0);
			
		return ret;
	}
	public boolean updateComment( RepositoryConnection con, URI uri, String entry, String oldtimestamp, String lang ) throws Exception {
		
		boolean ret = true;
		
		entry = trimEntry(entry);
		
		try {
			String actualEntry = selectComment( uri, lang );
			String oldEntry = selectComment( Mapper.selectVersionUri(uri, oldtimestamp), lang );
			String newEntry = entry; 

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					oldEntry );	
				
				//if( entry.equals("") )
				//	throw new RequiredFieldException( Labels.COMMENT.getNameSingular() );
				VersioningMapper.archiveRemoveInsertString(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						RDFS.COMMENT, 
						lang);

				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectComment( uri, lang ) );	
			}
		}
		catch( Exception ex ) {
			this.addExceptionComment( ex );
		}
		
		if( this.exceptionsComment!=null )
			ret = false;
		
		return ret;
	}
	public boolean insertComment( RepositoryConnection con, URI uri, String entry, String lang ) throws Exception {

		boolean ret = true;
		
		entry = trimEntry(entry);	
		
		try {
			
			con.add( uri, RDFS.COMMENT, new LiteralImpl(entry, lang));
		}
		catch( Exception ex ) {
			this.addExceptionComment( ex );
		}
		
		if( this.exceptionsComment!=null )
			ret = false;
		
		return ret;
	}
	
	public static ArrayList<URI> selectSeeAlso( URI uri ) throws Exception {
		return generateURIObjects( uri, Vocabulary.SEEALSO );
	}
	public boolean updateSeeAlso( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = true;
		
		entry = trimEntry(entry);
		
		try {
	
			ArrayList<URI> actualEntry = selectSeeAlso( uri ); 
			ArrayList<URI> oldEntry = selectSeeAlso( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry); 

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertURIs(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.SEEALSO );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectSeeAlso( uri ) );
			}
		}
		catch( Exception ex ) {
			this.addExceptionSeeAlsos( ex );
		}
		
		if( this.exceptionsSeeAlsos!=null )
			ret = false;
		
		return ret;
	}
	public boolean insertSeeAlso( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = true;
		
		entry = trimEntry(entry);	
		
		try {
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry);
			
			AbstractDataConnector.addURIs(con, uri, Vocabulary.SEEALSO, newEntry );
		}
		catch( Exception ex ) {
			this.addExceptionSeeAlsos( ex );
		}
		
		if( this.exceptionsSeeAlsos!=null )
			ret = false;
		
		return ret;
	}
	public boolean insertSeeAlso( RepositoryConnection con, URI uri, ArrayList<URI> newEntry ) throws Exception {

		boolean ret = true;
		
		try {
			
			AbstractDataConnector.addURIs(con, uri, Vocabulary.SEEALSO, newEntry );
		}
		catch( Exception ex ) {
			this.addExceptionSeeAlsos( ex );
		}
		
		if( this.exceptionsSeeAlsos!=null )
			ret = false;
		
		return ret;
	}
	
	public static ArrayList<String> selectImages( URI uri ) throws Exception {
		return generateLiteralObjects( uri, Vocabulary.FLICKRIMAGE, null );
	}
	public boolean updateImages( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = true;
		
		entry = trimEntry(entry);
		
		try {
	
			ArrayList<String> actualEntry = selectImages( uri ); 
			ArrayList<String> oldEntry = selectImages( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<String> newEntry = HelperFunctions.extractStrings(entry); 

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertStrings(
						con, 
						actualEntry,
						newEntry, 
						uri, 
						Vocabulary.FLICKRIMAGE );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectImages( uri ) );
			}
		}
		catch( Exception ex ) {
			this.addExceptionsImages( ex );
		}
		
		if( this.exceptionsImages!=null )
			ret = false;
		
		return ret;
	}
	public boolean insertImages( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			ArrayList<String> newEntry = HelperFunctions.extractStrings(entry); 
			
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.FLICKRIMAGE, newEntry );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsImages( ex );
		}
		
		return ret;
	}
	public boolean insertImages( RepositoryConnection con, URI uri, ArrayList<String> newEntry ) throws Exception {

		boolean ret = false;
		
		try {
			
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.FLICKRIMAGE, newEntry );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsImages( ex );
		}
		
		return ret;
	}
	
	public static ArrayList<String> selectVideos( URI uri ) throws Exception {
		return generateLiteralObjects( uri, Vocabulary.YOUTUBEVIDEO, null );
	}	
	public boolean updateVideos( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
	
			ArrayList<String> actualEntry = selectVideos( uri ); 
			ArrayList<String> oldEntry = selectVideos( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<String> newEntry = HelperFunctions.extractStrings(entry); 

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertStrings(
						con, 
						actualEntry,
						newEntry, 
						uri, 
						Vocabulary.YOUTUBEVIDEO );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectVideos( uri ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsVideos( ex );
		}
		
		return ret;
	}
	public boolean insertVideos( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			ArrayList<String> newEntry = HelperFunctions.extractStrings(entry); 
			
			AbstractDataConnector.addLiterals(con, uri, Vocabulary.YOUTUBEVIDEO, newEntry );
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsVideos( ex );
		}
		
		return ret;
	}
	public boolean insertVideos( RepositoryConnection con, URI uri, ArrayList<String> newEntry ) throws Exception {

		boolean ret = false;
		
		try {

			AbstractDataConnector.addLiterals(con, uri, Vocabulary.YOUTUBEVIDEO, newEntry );
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsVideos( ex );
		}
		
		return ret;
	}
	
	public static ArrayList<String> selectSynonyms( URI uri ) throws Exception {
		return generateLiteralObjects( uri, Vocabulary.SYNONYM, null );
	}
	public boolean updateSynonyms( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		

		try {
				
			ArrayList<String> actualEntry = selectSynonyms( uri );
			ArrayList<String> oldEntry = selectSynonyms( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<String> newEntry = HelperFunctions.extractStrings( 
											entry, 
											HelperFunctions.SYNONYMSPLITTER );
			
			checkSynonymsFormat( newEntry );

			if( exceptionsSynonyms==null && isOneEntryDifferent( actualEntry, newEntry ) ) {
				
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );	
				
				VersioningMapper.archiveRemoveInsertStrings(
						con, 
						actualEntry,
						newEntry, 
						uri, 
						Vocabulary.SYNONYM );				

				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectSynonyms( uri ) );	
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsSynonyms( ex );
		}
		
		return ret;
	}
	public boolean insertSynonyms( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			ArrayList<String> newEntry = HelperFunctions.extractStrings( 
											entry, 
											HelperFunctions.SYNONYMSPLITTER );

			checkSynonymsFormat( newEntry );

			if( exceptionsSynonyms==null ) {
				
				AbstractDataConnector.addLiterals(con, uri, Vocabulary.SYNONYM, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsSynonyms( ex );
		}
		
		return ret;
	}
	public boolean insertSynonyms( RepositoryConnection con, URI uri, ArrayList<String> newEntry ) throws Exception {

		boolean ret = false;
		
		try {

			checkSynonymsFormat( newEntry );

			if( exceptionsSynonyms==null ) {
			
				AbstractDataConnector.addLiterals(con, uri, Vocabulary.SYNONYM, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsSynonyms( ex );
		}
		
		return ret;
	}
	protected void checkSynonymsFormat( ArrayList<String> newEntry ) {

		if( newEntry!=null ) {
			Iterator<String> iter = newEntry.iterator();
			while( iter.hasNext() ) {
				
				String entry = iter.next();
				
				if( entry.contains("\"") || entry.contains("'") )
					this.addExceptionsSynonyms( new WrongFormatException( "Colons are not allowed!" ) );
			}
		}
	}
	
	public static ArrayList<String> selectTags( URI uri ) throws Exception {
		return generateLiteralObjects( uri, Vocabulary.HASTAG, null );
	}
	public boolean updateTags( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
			
			ArrayList<String> actualEntry = selectTags( uri );
			ArrayList<String> oldEntry = selectTags( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<String> newEntry = HelperFunctions.extractStrings( 
											entry, 
											HelperFunctions.TAGSSPLITTER );

			checkTagsFormat( newEntry );

			if( exceptionsTags==null && isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );			
				
				VersioningMapper.archiveRemoveInsertStrings(
						con, 
						actualEntry,
						newEntry, 
						uri, 
						Vocabulary.HASTAG );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectTags( uri ) );	
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsTags( ex );
		}
		
		return ret;
	}
	public boolean insertTags( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			ArrayList<String> newEntry = HelperFunctions.extractStrings( 
											entry, 
											HelperFunctions.TAGSSPLITTER );
			checkTagsFormat( newEntry );
			if( exceptionsTags==null ) {
			
				AbstractDataConnector.addLiterals(con, uri, Vocabulary.HASTAG, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsTags( ex );
		}
		
		return ret;
	}
	public boolean insertTags( RepositoryConnection con, URI uri, ArrayList<String> newEntry ) throws Exception {

		boolean ret = false;
		
		try {
			
			checkTagsFormat( newEntry );

			if( exceptionsTags==null ) {
				AbstractDataConnector.addLiterals(con, uri, Vocabulary.HASTAG, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsTags( ex );
		}
		
		return ret;
	}
	protected void checkTagsFormat( ArrayList<String> newEntry ) {

		if( newEntry!=null ) {
			Iterator<String> iter = newEntry.iterator();
			while( iter.hasNext() ) {
				
				String entry = iter.next();
				
				if( entry.contains("\"") || entry.contains("'") ) {
					
					this.addExceptionsTags( new WrongFormatException( "Colons are not allowed!" ) );
				}
			}
		}
	}
	
	public static ArrayList<TranslationContainer> selectTranslation( URI uri ) throws Exception {
		return generateTranslations( uri );
	}
	public boolean updateTranslations( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
	
			ArrayList<TranslationContainer> actualEntry = selectTranslation( uri );
			ArrayList<TranslationContainer> oldEntry = selectTranslation( Mapper.selectVersionUri(uri, oldtimestamp) );
			ArrayList<TranslationContainer> newEntry = HelperFunctions.extractTranslations( entry );

			checkTranslationsFormat( newEntry );

			if( exceptionsTranslations==null && isOneEntryDifferent( actualEntry, newEntry ) ) {
				
				Mapper.checkIfStoredInMeantime( 
						HelperFunctions.concatTranslations( actualEntry ),
						HelperFunctions.concatTranslations( oldEntry ) );
				
				VersioningMapper.archiveRemoveInsertTranslations(
						con, 
						actualEntry,
						newEntry, 
						uri );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					HelperFunctions.concatTranslations( actualEntry ), 
					HelperFunctions.concatTranslations( selectTranslation( uri ) ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsTranslations( ex );
		}
		
		return ret;
	}
	public boolean insertTranslations( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			ArrayList<TranslationContainer> newEntry = HelperFunctions.extractTranslations( entry );
			
			checkTranslationsFormat( newEntry );

			if( exceptionsTranslations==null ) {
			
				AbstractDataConnector.addTranslations( con, uri, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsTranslations( ex );
		}
		
		return ret;
	}
	public boolean insertTranslations( RepositoryConnection con, URI uri, ArrayList<TranslationContainer> newEntry ) throws Exception {

		boolean ret = false;
		
		try {
			
			checkTranslationsFormat( newEntry );

			if( exceptionsTranslations==null ) {
			
				AbstractDataConnector.addTranslations( con, uri, newEntry );
				ret = true;
			}
		}
		catch( Exception ex ) {
			this.addExceptionsTranslations( ex );
		}
		
		return ret;
	}
	protected void checkTranslationsFormat( ArrayList<TranslationContainer> newEntry ) {

		if( newEntry!=null ) {
			Iterator<TranslationContainer> iter = newEntry.iterator();
			while( iter.hasNext() ) {
				
				String entry = iter.next().getValue();

				if( entry.contains("\"") || entry.contains("'") )
					this.addExceptionsTranslations( new WrongFormatException( "Colons are not allowed!" ) );
			}
		}
	}
	
	public static String selectExample( URI uri ) throws Exception {
		String ret = null;
		
		ArrayList<String> tmp = generateLiteralObjects( uri, Vocabulary.HASEXAMPLE, null );
		
		if( tmp!=null && tmp.size()!=0 )
			ret = (String)tmp.get(0);
			
		return ret;
	}	
	public boolean updateExample( RepositoryConnection con, URI uri, String entry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
	
			String actualEntry = selectExample(uri); 
			String oldEntry = selectExample( Mapper.selectVersionUri(uri, oldtimestamp) );
			String newEntry = entry; 

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertString( 
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.HASEXAMPLE,
						null );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectExample( uri ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsExamples( ex );
		}
		
		return ret;
	}
	public boolean insertExample( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);		
		
		try {
			
			con.add( uri, Vocabulary.HASEXAMPLE, new LiteralImpl(entry));
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionsExamples( ex );
		}
		
		return ret;
	}

	protected static ArrayList<URI> selectBelongsToOM( URI uri ) throws Exception {
		// Classes cannot be archived when changes were done in belongsToOm. So always the most actual version should be shown.
		
		// return generateURIObjects( uri, Vocabulary.BELONGSTOONTOLOGYMODULE, Vocabulary.OntologyModule );
		
		// is same as following (following is slightely faster):
		return AbstractDataConnector.selectObjects(VersioningMapper.getMostActualVersion(uri), Vocabulary.BELONGSTOONTOLOGYMODULE, Vocabulary.ONTOLOGYMODULE );
	}
	protected boolean updateBelongsToOM( RepositoryConnection con, URI uri, String entry, String oldentry ) throws Exception {

		boolean ret = false;
		
		entry = trimEntry(entry);
		
		try {
			
			ArrayList<URI> actualEntry = selectBelongsToOM( uri );
			// ArrayList<URI> oldEntry = selectBelongsToOM( Mapper.selectVersionUri(uri, oldtimestamp) );
			// actualEntry and oldEntry will always be the same as never c/v0 belongsToOm om would exist. belongsToOm are archived with the OM and not with the class!
			ArrayList<URI> oldEntry = HelperFunctions.extractURIs(oldentry);			
			ArrayList<URI> newEntry = HelperFunctions.extractURIs(entry);
			
			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
		
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertSubjects(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.BELONGSTOONTOLOGYMODULE );
			}
			ret = true;	
		}
		catch( Exception ex ) {
			this.addExceptionBelongsToOM( ex );
		}
		
		return ret;	
	}
	protected boolean updateBelongsToOM( RepositoryConnection con, URI uri, ArrayList<URI> newEntry, String oldentry ) throws Exception {

		boolean ret = false;
		
		try {
			
			ArrayList<URI> actualEntry = selectBelongsToOM( uri );
			// ArrayList<URI> oldEntry = selectBelongsToOM( Mapper.selectVersionUri(uri, oldtimestamp) );
			// actualEntry and oldEntry will always be the same as never c/v0 belongsToOm om would exist. belongsToOm are archived with the OM and not with the class!
			ArrayList<URI> oldEntry = HelperFunctions.extractURIs(oldentry);

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertSubjects(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						Vocabulary.BELONGSTOONTOLOGYMODULE );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectBelongsToOM( uri ) );
			}
			ret = true;	
		}
		catch( Exception ex ) {
			this.addExceptionBelongsToOM( ex );
		}
		
		return ret;	
	}
	/*
	protected boolean insertBelongsToOM( RepositoryConnection con, URI uri, String entry ) throws Exception {

		boolean ret = false;
		this.exceptionsBelongsToOM = null;
		
		try {

			ArrayList<URI> newEntry = HelperFunctions.extractURIs( entry );
			
			VersioningMapper.archiveRemoveInsertSubjects(con, null, newEntry, uri, Vocabulary.BELONGSTOONTOLOGYMODULE );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionBelongsToOM( ex );
		}
		
		return ret;
	}
	protected boolean insertBelongsToOM( RepositoryConnection con, URI uri, ArrayList<URI> newEntry ) throws Exception {

		boolean ret = false;
		this.exceptionsBelongsToOM = null;
		
		try {
			
			VersioningMapper.archiveRemoveInsertSubjects(con, null, newEntry, uri, Vocabulary.BELONGSTOONTOLOGYMODULE );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionBelongsToOM( ex );
		}
		
		return ret;
	}
	*/
	

	protected static URI transformToURI( String id ) throws UriDoesNotExistException {
		
		URI ret = null;
		
		if( id==null || id.equals("") )	{
			throw new UriDoesNotExistException();
		}
		else {
			ret = new URIImpl( id );
		}
		
		return ret;
	}
	
	public static boolean checkIfStoredInMeantime( String actual, String userentry ) throws StoredInMeantimeException {

		boolean ret = false;
		
		if( actual==null)
			actual = "";
		
		if( userentry==null )
			userentry = "";
		
		if( !actual.equals( userentry ) )
			throw new StoredInMeantimeException();
		else
			ret = true;
		
		return ret;
	}
	
	/**
	 * returns true if not stored in meantime
	 * @param actual
	 * @param userentry
	 * @return
	 * @throws StoredInMeantimeException
	 */
	public static boolean checkIfStoredInMeantime( ArrayList actual, ArrayList userentry ) throws StoredInMeantimeException {

		boolean ret = false;
		
		if( isOneEntryDifferent(actual, userentry) )
			throw new StoredInMeantimeException();
				
		return ret;
	}

	/**
	 * Returns true if minimum one entry in one list is not contained in the other list.
	 * @param actual
	 * @param userentry
	 * @return
	 */
	public static boolean isOneEntryDifferent( ArrayList actual, ArrayList userentry ) {

		boolean ret = true;
		
		
		if( actual==null)
			actual = new ArrayList();
		
		if( userentry==null )
			userentry = new ArrayList();
		
		if( actual.size()==0 && userentry.size()==0 ) {
			ret = false;
		}
		else {
			if( !actual.containsAll( userentry ) || !userentry.containsAll( actual )  )
				ret = true;
			else
				ret = false;			
		}
		
		return ret;
	}
	

	public static boolean isOneEntryDifferent( String actual, String userentry ) {

		boolean ret = true;
		
		if( actual==null)
			actual = "";
		
		if( userentry==null )
			userentry = "";
		
		if( actual.equals(userentry))
			ret = false;
		
		return ret;
		
	}
	

	/**
	 * 
	 * @param uri	the most actual URI
	 * @param oldtimestamp
	 * @return
	 * @throws StoredInMeantimeException
	 */
	public static URI selectVersionUri( URI uri, String oldtimestamp ) throws Exception {
		
		// BUG: 2 Elements could have the same timestamp... So include the uri 
		URI ret = null;
		
		URI moreLaterVersion = uri;
		
		if( oldtimestamp!=null ) {
			
			do {
	
				String tmp = AbstractDataConnector.selectLiteralObject( moreLaterVersion, Vocabulary.HASVERSIONDATETIME, null );
			
				if( tmp==null || tmp.equals( oldtimestamp ) ) {
					break;
				}		
	
				moreLaterVersion = AbstractDataConnector.selectObject( moreLaterVersion, Vocabulary.HASPREVIOUSVERSION );
				
			} while( moreLaterVersion!=null );
	
			ret = moreLaterVersion;
			
			// URI ret = AbstractDataConnector.selectSubject( Vocabulary.HASVERSIONDATETIME, oldtimestamp );
		}
		
		return ret;
	}
	



	protected static ArrayList<URI> selectPartlyOverlapsWith( URI uri, URI prop ) throws Exception {
		
		return generateURIObjects( uri, prop );
	}	
	protected boolean updatePartlyOverlapsWith( RepositoryConnection con, URI uri, URI prop, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		try {
	
			ArrayList<URI> actualEntry = selectPartlyOverlapsWith( uri, prop );
			ArrayList<URI> oldEntry = selectPartlyOverlapsWith( Mapper.selectVersionUri(uri, oldtimestamp), prop );

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				updatePool( con, uri, prop, newEntry, actualEntry );
				
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					selectPartlyOverlapsWith( uri, prop ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionPartlyOverlapsWith( ex );
		}
		
		return ret;		
	}
	


	protected static ArrayList<URI> selectSameAs( URI uri, URI prop ) throws Exception {
		
		return generateURIObjects( uri, prop );
	}	
	protected boolean updateSameAs( RepositoryConnection con, URI uri, URI prop, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		try {
	
			ArrayList<URI> actualEntry = selectSameAs( uri, prop );
			ArrayList<URI> oldEntry = selectSameAs( Mapper.selectVersionUri(uri, oldtimestamp), prop );

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				updatePool( con, uri, prop, newEntry, actualEntry );
				
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					selectSameAs( uri, prop ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionSameAs( ex );
		}
		
		return ret;		
	}
	
	
	private void updatePool( RepositoryConnection con, URI uri, URI prop, ArrayList<URI> newEntry, ArrayList<URI> actualEntry ) throws Exception {

		ArrayList<URI> Aalt = actualEntry;
		ArrayList<URI> Aneu = newEntry;
		ArrayList<URI> oldPool = null; // pool of related old elements
		ArrayList<URI> newPool = null; // pool of related new elements
		ArrayList<URI> delta = null; // pool of related new elements
		
		// PREPARATION
		oldPool = new ArrayList<URI>();
		if( Aalt!=null )
			oldPool.addAll( Aalt );
		oldPool.add( uri );
		
		// generate UNION of all related elements:
		newPool = new ArrayList<URI>();
		if( Aneu!=null ) {
			newPool.addAll( Aneu );
			for( URI i : Aneu ) {
				
				ArrayList<URI> a2 = generateURIObjects( i, prop );
				if( a2!=null ) {
					
					if( Aalt!=null )
						a2.removeAll( Aalt );
					
					for( URI i2 : a2 ) {

						if( !newPool.contains( i2 ) )
							newPool.add( i2 );
					}
				}
			}
		}
		if( !newPool.contains( uri ))
			newPool.add( uri );
		
		// determine difference (union minus intersect):
		delta = new ArrayList<URI>();
		if( Aneu!=null ) {
			for( URI i : Aneu ) {

				if( oldPool==null || !oldPool.contains( i ) )
					delta.add( i );
			}
		}
		if( oldPool!=null ) {
			for( URI i : oldPool ) {

				if( Aneu==null || !Aneu.contains( i ) )
					delta.add( i );
			}
		}
		
		// union of Aalt and newPool
		ArrayList<URI> union = new ArrayList<URI>(); // pool of related new elements
		if( newPool!=null ) {
			union.addAll( newPool );
		}
		if( Aalt!=null ) {
			for( URI e: Aalt ) {
				if( !union.contains( e ))
					union.add( e );
			}
		}
		
		// MODIFICATION
		for( URI e : union ) {
			
			if( delta.contains( e ) ) {
				
				if( !newPool.contains( e ) ) {
					// e was removed
					
					VersioningMapper.archiveRemoveInsertURIs(
							con, 
							generateURIObjects( e, prop ),
							null, 
							e, 
							prop );
				}
				else {
					// e was added
					
					ArrayList<URI> t = new ArrayList<URI>( newPool );
					t.remove( e );
					
					VersioningMapper.archiveRemoveInsertURIs(
							con, 
							generateURIObjects( e, prop ),
							t, 
							e, 
							prop );
				}
			}
			else {

				// It should not be archived but replaced!
				ArrayList<URI> t = new ArrayList<URI>( newPool );
				t.remove( e );
				
				//remove old values 
				con.remove(e, prop, null);
				
				//insert new values
				if( t!=null ) {
					AbstractDataConnector.addURIs(con, e, prop, t);
				}
				
				/* archiving would be:
				
				VersioningMapper.archiveRemoveInsertURIs(
						con, 
						generateURIObjects( e, prop ),
						t, 
						e, 
						prop );	
				*/			
			}
		}

	}
	
	

	protected static ArrayList<URI> selectNarrowerThan( URI uri, URI prop ) throws Exception {
		
		return generateURIObjects( uri, prop );
	}	
	protected boolean updateNarrowerThan( RepositoryConnection con, URI uri, URI prop, ArrayList<URI> newEntry, String oldtimestamp ) throws Exception {

		boolean ret = false;
		
		try {
	
			ArrayList<URI> actualEntry = selectNarrowerThan( uri, prop );
			ArrayList<URI> oldEntry = selectNarrowerThan( Mapper.selectVersionUri(uri, oldtimestamp), prop );

			if( isOneEntryDifferent( actualEntry, newEntry ) ) {
				Mapper.checkIfStoredInMeantime( 
					actualEntry,
					oldEntry );
				
				VersioningMapper.archiveRemoveInsertURIs(
						con, 
						actualEntry, 
						newEntry, 
						uri, 
						prop );
				
				// this will block other threads from overtaking this thread; do not know exectely why but it works...
				Mapper.checkIfStoredInMeantime( 
					actualEntry, 
					selectNarrowerThan( uri, prop ) );
			}
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionNarrowerThan( ex );
		}
		
		return ret;			
	}
	protected boolean insertNarrowerThan( RepositoryConnection con, URI uri, URI prop, ArrayList<URI> entry ) throws Exception {

		boolean ret = false;
		
		try {
			
			AbstractDataConnector.addURIs(con, uri, prop, entry );
			
			ret = true;
		}
		catch( Exception ex ) {
			this.addExceptionNarrowerThan( ex );
		}
		
		return ret;
	}

}
